home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / rcs4 / source / partime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-07  |  16.0 KB  |  523 lines

  1. /*
  2.  * PARTIME        parse date/time string into a TM structure
  3.  *
  4.  * Usage:
  5.  *      #include "time.h"             -- expanded tm structure
  6.  *    char *str; struct tm *tp;
  7.  *    partime(str,tp);
  8.  * Returns:
  9.  *    0 if parsing failed
  10.  *    else time values in specified TM structure (unspecified values
  11.  *        set to TMNULL)
  12.  * Notes:
  13.  *    This code is quasi-public; it may be used freely in like software.
  14.  *    It is not to be sold, nor used in licensed software without
  15.  *    permission of the author.
  16.  *    For everyone's benefit, please report bugs and improvements!
  17.  *     Copyright 1980 by Ken Harrenstien, SRI International.
  18.  *    (ARPANET: KLH @ SRI)
  19.  */
  20.  
  21. /* Hacknotes:
  22.  *    If parsing changed so that no backup needed, could perhaps modify
  23.  *        to use a FILE input stream.  Need terminator, though.
  24.  *    Perhaps should return 0 on success, else a non-zero error val?
  25.  *    Flush AMPM from TM structure and handle locally within PARTIME,
  26.  *        like midnight/noon?
  27.  */
  28.  
  29. #ifndef lint
  30. static char rcsid[]="$Header: d:/rcs/rcs/partime.c 5.4 91/02/07 13:58:40 ROOT_DOS Exp $";
  31. #endif
  32.  
  33. /* $Log:    partime.c $
  34.  * Revision 5.4  91/02/07  13:58:40  ROOT_DOS
  35.  * Removed appalling use of unsigned int and int to hold pointers !
  36.  * 
  37.  * Revision 5.3  90/07/15  20:24:09  lfk
  38.  * checked in with -k by ROOT_DOS at 91.02.07.11.44.51.
  39.  * 
  40.  * Revision 5.3  90/07/15  20:24:09  lfk
  41.  * Most major fixes added between rev 5.1 and rev 5.5:
  42.  *     signals fixed so they work on MS-DOS
  43.  *     Added MKS arguments code so argv can be large
  44.  *     added code to handle slashes a'la Unix
  45.  *     added more file extensions to system from MS-DOS
  46.  * 
  47.  * Revision 5.2  90/07/15  11:32:07  ROOT_DOS
  48.  * DOS version of RCS 4.0 checked in for MODS
  49.  * by lfk@athena.mit.edu
  50.  * Also update to MSC 6.0
  51.  * 
  52.  * Revision 1.4  89/05/01  14:48:46  narten
  53.  * fixed #ifdef DEBUG construct
  54.  * 
  55.  * Revision 1.3  88/11/08  12:02:15  narten
  56.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  57.  * 
  58.  * Revision 1.3  88/08/28  14:53:40  eggert
  59.  * Remove unportable "#endif XXX"s.
  60.  * 
  61.  * Revision 1.2  87/03/27  14:21:53  jenkins
  62.  * Port to suns
  63.  * 
  64.  * Revision 1.1  84/01/23  14:50:07  kcs
  65.  * Initial revision
  66.  * 
  67.  * Revision 1.1  82/05/06  11:38:26  wft
  68.  * Initial revision
  69.  * 
  70.  */
  71.  
  72. #include <stdio.h>
  73. #include <ctype.h>
  74. #include "time.h"
  75.  
  76. #ifndef lint
  77. static char timeid[] = TIMEID;
  78. #endif
  79.  
  80. struct tmwent {
  81.     char *went;
  82.     long wval;    /* must be big enough to hold pointer or integer */
  83.     char wflgs;
  84.     char wtype;
  85. };
  86.     /* wflgs */
  87. #define TWSPEC 01    /* Word wants special processing */
  88. #define TWTIME 02    /* Word is a time value (absence implies date) */
  89. #define TWDST  04    /* Word is a DST-type timezone */
  90. #define TW1200 010    /* Word is NOON or MIDNIGHT (sigh) */
  91.  
  92. int pt12hack();
  93. int ptnoise();
  94. struct tmwent tmwords [] = {
  95.     {"january",      0, 0, TM_MON},
  96.     {"february",     1, 0, TM_MON},
  97.     {"march",        2, 0, TM_MON},
  98.     {"april",        3, 0, TM_MON},
  99.     {"may",          4, 0, TM_MON},
  100.     {"june",         5, 0, TM_MON},
  101.     {"july",         6, 0, TM_MON},
  102.     {"august",       7, 0, TM_MON},
  103.     {"september",    8, 0, TM_MON},
  104.     {"october",      9, 0, TM_MON},
  105.     {"november",     10, 0, TM_MON},
  106.     {"december",     11, 0, TM_MON},
  107.  
  108.     {"sunday",       0, 0, TM_WDAY},
  109.     {"monday",       1, 0, TM_WDAY},
  110.     {"tuesday",      2, 0, TM_WDAY},
  111.     {"wednesday",    3, 0, TM_WDAY},
  112.     {"thursday",     4, 0, TM_WDAY},
  113.     {"friday",       5, 0, TM_WDAY},
  114.     {"saturday",     6, 0, TM_WDAY},
  115.  
  116.     {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
  117.     {"gst",          0*60, TWTIME, TM_ZON},
  118.     {"gdt",          0*60, TWTIME+TWDST, TM_ZON},     /* ?? */
  119.  
  120.     {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
  121.     {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
  122.     {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
  123.     {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
  124.     {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
  125.     {"yst",          9*60, TWTIME, TM_ZON},   /* Yukon */
  126.     {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
  127.     {"bst",          11*60, TWTIME, TM_ZON},  /* Bering */
  128.  
  129.     {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
  130.     {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
  131.     {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
  132.     {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
  133.     {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
  134.     {"ydt",          9*60, TWTIME+TWDST, TM_ZON},     /* Yukon */
  135.     {"hdt",          10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii */
  136.     {"bdt",          11*60, TWTIME+TWDST, TM_ZON},    /* Bering */
  137.  
  138.     {"daylight",     1, TWTIME+TWDST, TM_ZON},        /* Local Daylight */
  139.     {"standard",     1, TWTIME, TM_ZON},      /* Local Standard */
  140.     {"std",          1, TWTIME, TM_ZON},      /*   "       "    */
  141.  
  142.     {"am",           1, TWTIME, TM_AMPM},
  143.     {"pm",           2, TWTIME, TM_AMPM},
  144.     {"noon",         12,TWTIME+TW1200, 0},    /* Special frobs */
  145.     {"midnight",     0, TWTIME+TW1200, 0},
  146.     {"at",           (long)ptnoise, TWSPEC, 0},    /* Noise word */
  147.  
  148.     {0, 0, 0, 0},             /* Zero entry to terminate searches */
  149. };
  150.  
  151. #define TMWILD (-2)    /* Value meaning item specified as wild-card */
  152.             /* (May use someday...) */
  153.  
  154. struct token {
  155.     char *tcp;    /* pointer to string */
  156.     int tcnt;    /* # chars */
  157.     char tbrk;    /* "break" char */
  158.     char tbrkl;    /* last break char */
  159.     char tflg;    /* 0 = alpha, 1 = numeric */
  160.     union {         /* Resulting value; */
  161.         int tnum;/* either a #, or */
  162.         struct tmwent *ttmw;/* ptr to a tmwent. */
  163.     } tval;
  164. };
  165.  
  166. extern struct tmwent *ptmatchstr();
  167.  
  168. partime(astr, atm)
  169. char *astr;
  170. struct tm *atm;
  171. {    register int *tp;
  172.     register struct tmwent *twp;
  173.     register int i;
  174.     struct token btoken, atoken;
  175.     char *cp, ch;
  176.     int ord, midnoon;
  177.     int (*aproc)();
  178.  
  179.     tp = (int *)atm;
  180.     zaptime(tp);             /* Initialize the TM structure */
  181.     midnoon = TMNULL;        /* and our own temp stuff */
  182.     btoken.tcnt = btoken.tbrkl = 0;
  183.     btoken.tcp = astr;
  184.  
  185. domore:
  186.     if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))    /* Get a token */
  187.       {     if(btoken.tval.tnum) return(0);         /* Read error? */
  188.         if(midnoon != TMNULL)            /* EOF, wrap up */
  189.             return(pt12hack(tp, midnoon));
  190.         return(1);                /* Win return! */
  191.       }
  192.     if(btoken.tflg == 0)        /* Alpha? */
  193.       {     twp = btoken.tval.ttmw;         /* Yes, get ptr to entry */
  194.         if(twp->wflgs&TWSPEC)        /* Special alpha crock */
  195.           {     aproc = (int (*) ()) (twp->wval);
  196.             if(!(*aproc)(tp, twp, &btoken))
  197.                 return(0);    /* ERR: special word err */
  198.             goto domore;
  199.           }
  200.         if(twp->wflgs&TW1200)
  201.             if(ptstash(&midnoon,(int)twp->wval))
  202.                 return(0);    /* ERR: noon/midnite clash */
  203.             else goto domore;
  204.         if(ptstash(&tp[twp->wtype],(int)twp->wval))
  205.             return(0);        /* ERR: val already set */
  206.         if(twp->wtype == TM_ZON)    /* If was zone, hack DST */
  207.             if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
  208.                 return(0);    /* ERR: DST conflict */
  209.         goto domore;
  210.       }
  211.  
  212.     /* Token is number.  Lots of hairy heuristics. */
  213.     if(btoken.tcnt >= 7)    /* More than 6 digits in string? */
  214.         return(0);    /* ERR: number too big */
  215.     if(btoken.tcnt == 6)    /* 6 digits = HHMMSS.  Needs special crock */
  216.       {            /* since 6 digits are too big for integer! */
  217.         i = (btoken.tcp[0]-'0')*10    /* Gobble 1st 2 digits */
  218.            + btoken.tcp[1]-'0';
  219.         btoken.tcnt = 2;        /* re-read last 4 chars */
  220.         goto coltime;
  221.       }
  222.  
  223.     i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  224.     if( btoken.tcnt == 5    /*  5 digits = HMMSS */
  225.      || btoken.tcnt == 3)    /*  3 digits = HMM   */
  226.       {    if(btoken.tcnt != 3)
  227.             if(ptstash(&tp[TM_SEC], i%100))
  228.                 return(0);    /* ERR: sec conflict */
  229.             else i /= 100;
  230. hhmm4:        if(ptstash(&tp[TM_MIN], i%100))
  231.             return(0);        /* ERR: min conflict */
  232.         i /= 100;
  233. hh2:            if(ptstash(&tp[TM_HOUR], i))
  234.             return(0);        /* ERR: hour conflict */
  235.         goto domore;
  236.       }
  237.  
  238.     if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  239.       {    if(tp[TM_YEAR] != TMNULL) goto hhmm4;    /* Already got yr? */
  240.         if(tp[TM_HOUR] != TMNULL) goto year4;    /* Already got hr? */
  241.         if((i%100) > 59) goto year4;        /* MM >= 60? */
  242.         if(btoken.tbrk == ':')            /* HHMM:SS ? */
  243.             if( ptstash(&tp[TM_HOUR],i/100)
  244.              || ptstash(&tp[TM_MIN], i%100))
  245.                 return(0);        /* ERR: hr/min clash */
  246.             else goto coltm2;        /* Go handle SS */
  247.         if(btoken.tbrk != ',' && btoken.tbrk != '/'
  248.           && ptitoken(btoken.tcp+btoken.tcnt,&atoken)    /* Peek */
  249.           && atoken.tflg == 0            /* alpha */
  250.           && (atoken.tval.ttmw->wflgs&TWTIME))  /* HHMM-ZON */
  251.             goto hhmm4;
  252.         if(btoken.tbrkl == '-'        /* DD-Mon-YYYY */
  253.           || btoken.tbrkl == ','    /* Mon DD, YYYY */
  254.           || btoken.tbrkl == '/'    /* MM/DD/YYYY */
  255.           || btoken.tbrkl == '.'    /* DD.MM.YYYY */
  256.           || btoken.tbrk == '-'        /* YYYY-MM-DD */
  257.             ) goto year4;
  258.         goto hhmm4;            /* Give up, assume HHMM. */
  259.       }
  260.  
  261.     /* From this point on, assume tcnt == 1 or 2 */
  262.     /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
  263.     if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  264.         goto coltime;        /*  must be part of time. */
  265.     if(i > 31) goto yy2;        /* If >= 32, only YY poss. */
  266.  
  267.     /* Check for numerical-format date */
  268.     for (cp = "/-."; ch = *cp++;)
  269.       {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  270.         if(btoken.tbrk == ch)            /* "NN-" */
  271.           {    if(btoken.tbrkl != ch)
  272.               {    if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
  273.                   && atoken.tflg == 0
  274.                   && atoken.tval.ttmw->wtype == TM_MON)
  275.                     goto dd2;
  276.                 if(ord)goto mm2; else goto dd2; /* "NN-" */
  277.               }                /* "-NN-" */
  278.             if(tp[TM_DAY] == TMNULL
  279.             && tp[TM_YEAR] != TMNULL)    /* If "YY-NN-" */
  280.                 goto mm2;        /* then always MM */
  281.             if(ord)goto dd2; else goto mm2;
  282.           }
  283.         if(btoken.tbrkl == ch            /* "-NN" */
  284.           && tp[ord ? TM_MON : TM_DAY] != TMNULL)
  285.             if(tp[ord ? TM_DAY : TM_MON] == TMNULL)    /* MM/DD */
  286.                 if(ord)goto dd2; else goto mm2;
  287.             else goto yy2;            /* "-YY" */
  288.       }
  289.  
  290.     /* At this point only YY, DD, and HH are left.
  291.      * YY is very unlikely since value is <= 32 and there was
  292.      * no numerical format date.  Make one last try at YY
  293.      * before dropping through to DD vs HH code.
  294.      */
  295.     if(btoken.tcnt == 2        /* If 2 digits */
  296.       && tp[TM_HOUR] != TMNULL    /* and already have hour */
  297.       && tp[TM_DAY] != TMNULL    /* and day, but  */
  298.       && tp[TM_YEAR] == TMNULL)    /* no year, then assume */
  299.         goto yy2;        /* that's what we have. */
  300.  
  301.     /* Now reduced to choice between HH and DD */
  302.     if(tp[TM_HOUR] != TMNULL) goto dd2;    /* Have hour? Assume day. */
  303.     if(tp[TM_DAY] != TMNULL) goto hh2;    /* Have day? Assume hour. */
  304.     if(i > 24) goto dd2;            /* Impossible HH means DD */
  305.     if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken))    /* Read ahead! */
  306.         if(atoken.tval.tnum) return(0); /* ERR: bad token */
  307.         else goto dd2;            /* EOF, assume day. */
  308.     if( atoken.tflg == 0        /* If next token is an alpha */
  309.      && atoken.tval.ttmw->wflgs&TWTIME)  /* time-spec, assume hour */
  310.         goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  311.  
  312. dd2:    if(ptstash(&tp[TM_DAY],i))    /* Store day (1 based) */
  313.         return(0);
  314.     goto domore;
  315.  
  316. mm2:    if(ptstash(&tp[TM_MON], i-1))    /* Store month (make zero based) */
  317.         return(0);
  318.     goto domore;
  319.  
  320. yy2:    i += 1900;
  321. year4:    if(ptstash(&tp[TM_YEAR],i))    /* Store year (full number) */
  322.         return(0);        /* ERR: year conflict */
  323.     goto domore;
  324.  
  325.     /* Hack HH:MM[[:]SS] */
  326. coltime:
  327.     if(ptstash(&tp[TM_HOUR],i)) return(0);
  328.     if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
  329.         return(!btoken.tval.tnum);
  330.     if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  331.     if(btoken.tcnt == 4)        /* MMSS */
  332.         if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
  333.           || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
  334.             return(0);
  335.         else goto domore;
  336.     if(btoken.tcnt != 2
  337.       || ptstash(&tp[TM_MIN],btoken.tval.tnum))
  338.         return(0);        /* ERR: MM bad */
  339.     if(btoken.tbrk != ':') goto domore;    /* Seconds follow? */
  340. coltm2:    if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
  341.         return(!btoken.tval.tnum);
  342.     if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  343.       || ptstash(&tp[TM_SEC], btoken.tval.tnum))
  344.         return(0);        /* ERR: SS bad */
  345.     goto domore;
  346. }
  347.  
  348. /* Store date/time value, return 0 if successful.
  349.  * Fails if entry already set to a different value.
  350.  */
  351. ptstash(adr,val)
  352. int *adr;
  353. {    register int *a;
  354.     if( *(a=adr) != TMNULL)
  355.         return(*a != val);
  356.     *a = val;
  357.     return(0);
  358. }
  359.  
  360. /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
  361.  * just prior to returning from partime.
  362.  */
  363. pt12hack(atp, aval)
  364. int *atp, aval;
  365. {    register int *tp, i, h;
  366.     tp = atp;
  367.     if (((i=tp[TM_MIN]) && i != TMNULL)    /* Ensure mins, secs */
  368.      || ((i=tp[TM_SEC]) && i != TMNULL))    /* are 0 or unspec'd */
  369.         return(0);            /* ERR: MM:SS not 00:00 */
  370.     i = aval;            /* Get 0 or 12 (midnite or noon) */
  371.     if ((h = tp[TM_HOUR]) == TMNULL    /* If hour unspec'd, win */
  372.      || h == 12)            /* or if 12:00 (matches either) */
  373.         tp[TM_HOUR] = i;    /* Then set time */
  374.     else if(!(i == 0        /* Nope, but if midnight and */
  375.         &&(h == 0 || h == 24)))    /* time matches, can pass. */
  376.             return(0);    /* ERR: HH conflicts */
  377.     tp[TM_AMPM] = TMNULL;        /* Always reset this value if won */
  378.     return(1);
  379. }
  380.  
  381. /* Null routine for no-op tokens */
  382.  
  383. ptnoise() { return(1); }
  384.  
  385. /* Get a token and identify it to some degree.
  386.  * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  387.  * hit error of some sort
  388.  */
  389.  
  390. ptitoken(astr, tkp)
  391. register struct token *tkp;
  392. char *astr;
  393. {
  394.     register char *cp;
  395.     register int i;
  396.  
  397.     tkp->tval.tnum = 0;
  398.     if(pttoken(astr,tkp) == 0)
  399. #ifdef DEBUG
  400.         {
  401.         VOID printf("EOF\n");
  402.         return(0);
  403.         }
  404. #else
  405.         return(0);
  406. #endif    
  407.     cp = tkp->tcp;
  408.  
  409. #ifdef DEBUG
  410.     i = cp[tkp->tcnt];
  411.     cp[tkp->tcnt] = 0;
  412.     VOID printf("Token: \"%s\" ",cp);
  413.     cp[tkp->tcnt] = i;
  414. #endif
  415.  
  416.     if(tkp->tflg)
  417.         for(i = tkp->tcnt; i > 0; i--)
  418.             tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
  419.     else
  420.       {     struct tmwent *ii = ptmatchstr(cp, tkp->tcnt, tmwords);
  421.         if(ii)
  422.             tkp->tval.ttmw = ii;
  423.         else
  424.             tkp->tval.tnum = -1;         /* Set -1 for error */
  425.  
  426. #ifdef DEBUG
  427.         if(!ii) VOID printf("Not found!\n");
  428. #endif
  429.  
  430.         if(!ii) return(0);
  431.       }
  432.  
  433. #ifdef DEBUG
  434.     if(tkp->tflg)
  435.         VOID printf("Val: %d.\n",tkp->tval.tnum);
  436.     else VOID printf("Found: \"%s\", val: %d., type %d\n",
  437.         tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  438. #endif
  439.  
  440.     return(1);
  441. }
  442.  
  443. /* Read token from input string into token structure */
  444. pttoken(astr,tkp)
  445. register struct token *tkp;
  446. char *astr;
  447. {
  448.     register char *cp;
  449.     register int c;
  450.  
  451.     tkp->tcp = cp = astr;
  452.     tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  453.     tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  454.  
  455.     while(c = *cp++)
  456.       {    switch(c)
  457.           {    case ' ': case '\t':    /* Flush all whitespace */
  458.                 while((c = *cp++) && isspace(c));
  459.                 cp--;        /* Drop thru to handle brk */
  460.             case '(': case ')':    /* Perhaps any non-alphanum */
  461.             case '-': case ',':    /* shd qualify as break? */
  462.             case '/': case ':': case '.':    /* Break chars */
  463.                 if(tkp->tcnt == 0)    /* If no token yet */
  464.                   {    tkp->tcp = cp;    /* ignore the brk */
  465.                     tkp->tbrkl = c;
  466.                       continue;    /* and go on. */
  467.                   }
  468.                 tkp->tbrk = c;
  469.                 return(tkp->tcnt);
  470.           }
  471.         if(tkp->tcnt == 0)        /* If first char of token, */
  472.             tkp->tflg = isdigit(c);    /*    determine type */
  473.           if(( isdigit(c) &&  tkp->tflg)    /* If not first, make sure */
  474.          ||(!isdigit(c) && !tkp->tflg))    /*    char matches type */
  475.             tkp->tcnt++;        /* Win, add to token. */
  476.         else {
  477.             cp--;            /* Wrong type, back up */
  478.             tkp->tbrk = c;
  479.             return(tkp->tcnt);
  480.           }
  481.       }
  482.     return(tkp->tcnt);        /* When hit EOF */
  483. }
  484.  
  485. struct tmwent *ptmatchstr(astr,cnt,astruc)
  486. char *astr;
  487. int cnt;
  488. struct tmwent *astruc;
  489. {    register char *cp, *mp;
  490.     register int c;
  491.     struct tmwent *lastptr;
  492.     int i;
  493.  
  494.     lastptr = 0;
  495.     for(;mp = astruc->went; astruc += 1)
  496.       {    cp = astr;
  497.         for(i = cnt; i > 0; i--)
  498.           {    switch((c = *cp++) ^ *mp++)    /* XOR the chars */
  499.               {    case 0: continue;    /* Exact match */
  500.                 case 040: if(isalpha(c))
  501.                     continue;
  502.               }
  503.             break;
  504.           }
  505.         if(i==0)
  506.             if(*mp == 0) return(astruc);    /* Exact match */
  507.             else if(lastptr) return(0);    /* Ambiguous */
  508.             else lastptr = astruc;        /* 1st ambig */
  509.       }
  510.     return(lastptr);
  511. }
  512.  
  513.  
  514.  
  515. zaptime(tp)
  516. register int *tp;
  517. /* clears tm structure pointed to by tp */
  518. {    register int i;
  519.     i = (sizeof (struct tm))/(sizeof (int));
  520.     do *tp++ = TMNULL;        /* Set entry to "unspecified" */
  521.     while(--i);            /* Faster than FOR */
  522. }
  523.